/*
 * 任务管理
 *
 * author: lishutong
 * site: https://lishutong1024.cn / https://lishutong1024.github.cn
 */
#include "os_task.h"
#include "os_plat.h"
#include "os_sched.h"
#include <string.h>

/**
 * 初始化等待结构
 */
void task_wait_init (task_wait_t * wait, os_event_t * event, void * reason) {
    wait->event = event;
    wait->reason = reason;
    wait->err = OS_ERR_OK;
}

/**
 * 初始化任务（内部使用）
 */
static os_err_t task_init (os_task_t * task, const char * name, task_entry_t entry, void *param,
                    int prio, void * stack, int stack_size, int flags) {
    // 任务名初始化复制
    if (name != OS_NULL) {
        strncpy(task->name, name, sizeof(task->name));
        task->name[sizeof(task->name) - 1] = '\0';
    } else {
        task->name[0] = '\0';
    }

    // 初始化任务的栈和context
#if OS_TASK_INFO_ENABLE || OS_SCHED_CHECK_TASK_STACK
    stack_size = stack_size & ~(sizeof(cpu_stack_t) - 1);       // 进行对齐
    memset(stack, OS_TASK_STACK_FILL, stack_size);
    task->start_stack = stack;
    task->stack_size = stack_size;
#endif
    task->flags = flags;
    task->prio = prio;
    task->delay_tick = 0;

    os_task_ctx_init(task, stack, stack_size, entry, param);

    os_list_item_init(&task->run_item, os_task_t, run_item);
    os_list_item_init(&task->info_item, os_task_t, info_item);
    os_sched_add_new(task);;
    task_wait_init(&task->event_wait, OS_NULL, OS_NULL);
    os_list_item_init(&task->event_item, os_task_t, event_item);
    return OS_ERR_OK;
}

/**
 * 初始化一个任务
 * @param task 任务结构
 * @param entry 任务入口函数
 * @param param 运行参数
 * @param prio 任务运行优先级
 * @param stack 栈的起始地址，内存的起始地址
 * @param stack_size 栈的大小，以字节为单位
*/
os_err_t os_task_init (os_task_t * task, const char * name, task_entry_t entry, void *param,
                    int prio, void * stack, int stack_size) {
    os_param_failed(task == OS_NULL, OS_ERR_PARAM);
    os_param_failed(entry == OS_NULL, OS_ERR_PARAM);
    os_param_failed((prio < 0) && (prio >= OS_TASK_IDLE_PRIO), OS_ERR_PARAM);
    os_param_failed(stack == OS_NULL, OS_ERR_PARAM);
    os_param_failed(stack_size <= sizeof(os_task_ctx_t), OS_ERR_PARAM);

    return task_init(task, name, entry, param, prio, stack, stack_size, 0);
}

#if OS_MEM_ENABLE

/**
 * 动态创建任务
 * @param task 任务结构
 * @param entry 任务入口函数
 * @param param 运行参数
 * @param prio 任务运行优先级
 * @param stack 栈的起始地址，内存的起始地址
 * @param stack_size 栈的大小，以字节为单位
 */
os_task_t * os_task_create (const char * name, task_entry_t entry, void *param,
                    int prio, int stack_size) {
    os_param_failed(entry == OS_NULL, OS_NULL);
    os_param_failed((prio < 0) && (prio >= OS_TASK_IDLE_PRIO), OS_NULL);
    os_param_failed(stack_size <= sizeof(os_task_ctx_t), OS_NULL);

    // 分配task结构
    os_task_t * task = (os_task_t *)os_mem_malloc(sizeof(os_task_t));
    if (task == OS_NULL) {
        os_dbg("error: alloc task failed");
        return OS_NULL;
    }

    // 分配栈空间，注意对齐到栈单元的大小
    stack_size = stack_size & ~(sizeof(cpu_stack_t) - 1);
    char * stack = (char *)os_mem_malloc(stack_size);
    if (stack == OS_NULL) {
        os_dbg("error: alloc stack failed");
        goto create_failed;
    }

    // 初始化task，注意记录为动态分配的结构
    os_err_t err = task_init(task, name, entry, param, prio, stack, stack_size, OS_FLAG_MEM_HEAP);
    if (err < 0) {
        os_dbg("error: init task failed.");
        goto create_failed;
    }

    return task;
create_failed:
    // 创建失败，要释放相应的内存空间
    os_mem_free(task);
    if (stack) {
        os_mem_free(stack);
    }
    return OS_NULL;
}
#endif

/**
 * 启动创建好的任务开始运行
 */
void os_task_start (os_task_t * task) {
    // 插入就绪队列
    os_int_status_t status = os_sched_int_lock();

    os_sched_set_ready(task);

    // 进行一次调度
    if (os_task_self() != OS_NULL) {
        os_sched_run();
    }
    os_sched_int_unlock(status);
}

/**
 * 结束当前任务运行
 * 该函数应当永远不会返回，且任务应当已经自行完成了一些清理工作
 * 任务块的释放，交由其它任务完成：TODO:
*/
#if OS_TASK_EXIT_ENABLE
void os_task_exit (void) {
    os_int_status_t status = os_sched_int_lock();

    // 从就绪队列中移除
    os_task_t * self = os_task_self();
    os_sched_remove_ready(self);
    os_sched_set_deleted(self);
    os_sched_remove(self);

    // 进行一次调度
    os_sched_run();
    os_sched_int_unlock(status);

    // 不应当运行到这里
    os_assert(0);
}
#endif

/**
 * 让当前任务延时指定毫秒
 * @param ms 延时的毫秒数
*/
void os_task_sleep (int ms) {
    os_int_status_t status = os_sched_int_lock();

    if (ms > 0) {
        // 从就绪队列移到延时队列，进行调度切换到其它任务运行
        os_task_t * self = os_task_self();
        os_sched_remove_ready(self);
        os_sched_set_delay(self, ms);

        // 切换到其它任务运行
        os_sched_run();
    }

    os_sched_int_unlock(status);
}

/**
 * 挂起指定的任务
*/
#if OS_TASK_SUSPEND_ENABLE
os_err_t os_task_suspend (os_task_t * task) {
    if (task == OS_NULL) {
        task = os_task_self();
    }

    os_int_status_t status = os_sched_int_lock();

    // 已经挂起，不允许重新执行
    if (task->flags & OS_TASK_STATE_SUSPEND) {
        os_sched_int_unlock(status);
        return OS_ERR_STATE;
    }

    if (task == os_task_self()) {
        // 当前任务，则从就绪队列中移除，然后加入到挂起队列中
        os_sched_remove_ready(task);
        os_sched_set_suspend(task);

        // 挂起自己时，需要进行一次调度
        os_sched_run();
    } else {
        // 从就绪队列移除
        if (task->flags & OS_TASK_STATE_RDY) {
            os_sched_remove_ready(task);
        }

        // 延时队列移除
        if (task->flags & OS_TASK_STATE_DELAYED) {
            os_sched_remove_delay(task);
        }

        // 阻塞队列移除
        //if (task->flags & OS_TASK_STATE_WAIT) {
            // todo:
            //os_event_wakeup_task(task.w);
        //}

        // 加入挂起队列
        os_sched_set_suspend(task);
    }

    os_sched_int_unlock(status);
    return OS_ERR_OK;
}

/**
 * 恢复指定的任务
 * 不考虑任务挂起之前的状态，直接将任务设置为就绪状态
 */
os_err_t os_task_resume (os_task_t * task) {
    os_param_failed(task == OS_NULL, OS_ERR_PARAM);

    os_int_status_t status = os_sched_int_lock();

    if (task->flags & OS_TASK_STATE_SUSPEND) {
        os_sched_remove_suspend(task);
        os_sched_set_ready(task);
        os_sched_run();
    }

    os_sched_int_unlock(status);
    return OS_ERR_OK;
}
#endif

#if OS_TASK_INFO_ENABLE
/**
 * 检查任务还有多少栓
*/
os_err_t os_task_stack_info (os_task_t * task, os_task_stack_info_t * info) {
    os_param_failed(info == OS_NULL, OS_ERR_PARAM);

    if (task == OS_NULL) {
        task = os_task_self();
    }

    os_int_status_t status = os_sched_int_lock();
    info->stack = task->start_stack;
    info->stack_size = task->stack_size;
    info->unused = 0;

    // 用与栈增长相反的方向来计算
#if OS_PLAT_STACK_GROW_HTL
    uint8_t * start = (uint8_t *)task->start_stack;
    for (int i = 0; i < info->stack_size; i++) {
        if (*start++ != OS_TASK_STACK_FILL) {
            break;
        }
        info->unused++;
    }
#else
    uint8_t * start = task->start_stack + task->stack_size - 1;
    for (int i = 0; i < info->stack_size; i++) {
        if (*start-- != OS_TASK_STACK_FILL) {
            break;
        }
        info->unused++;
    }
#endif // OS_PLAT_STACK_GROW_HTL

    os_sched_int_unlock(status);
    return OS_ERR_OK;
}
#endif
